/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.filter;

import es.silenus.detecta.Constants;

import es.silenus.detecta.model.Context;
import es.silenus.detecta.model.UserPrincipal;

import org.springframework.context.MessageSource;

import org.springframework.context.i18n.LocaleContextHolder;

import org.springframework.security.Authentication;

import org.springframework.security.context.SecurityContextHolder;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import org.springframework.web.filter.OncePerRequestFilter;

import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.io.IOException;

import java.util.Date;
import java.util.Locale;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * A filter that adds some values to be available in a template view.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 28-mar-2009 17:08:32
 */
public class TemplateFilter extends OncePerRequestFilter {
	/**
	 * The message source.
	 */
	private MessageSource messageSource;

	/**
	 * Retrieves the message source.
	 *
	 * @return the message source.
	 */
	protected final MessageSource getMessageSource() {
		if(messageSource == null) {
			synchronized(this) {
				WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

				messageSource = (MessageSource)wac.getBean("messageSource");
			}
		}

		return messageSource;
	}

	/**
	 * Same contract as for <code>doFilter</code>, but guaranteed to be just invoked once per request. Provides
	 * HttpServletRequest and HttpServletResponse arguments instead of the default ServletRequest and ServletResponse
	 * ones.
	 *
	 * @param request the request.
	 * @param response the response.
	 * @param chain the filter chain.
	 *
	 * @throws ServletException if something goes wrong.
	 * @throws IOException if an I/O error occurs.
	 */
	protected final void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)
		throws ServletException, IOException {
		// Wrap the request
		TemplateRequest wrappedRequest = new TemplateRequest(request);

		// Prepare context
		Context.setMessageSource(getMessageSource());
		Context.setUser(wrappedRequest.getPrincipal());

		// Prepare template variables
		request.setAttribute(Constants.LANGUAGE, wrappedRequest.getLocale().getLanguage());
		request.setAttribute(Constants.CONTEXT_PATH, request.getContextPath());
		request.setAttribute(Constants.DATE, new Date());

		// Continue request processing
		chain.doFilter(wrappedRequest, response);
	}

	/**
	 * Template request.
	 */
	public static final class TemplateRequest extends HttpServletRequestWrapper {
		/**
		 * The principal.
		 */
		private UserPrincipal principal;

/**
     * Constructor.
     *
     * @param request the request.
     */
		public TemplateRequest(final HttpServletRequest request) {
			super(request);

			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

			principal = null;

			if((authentication != null) && (authentication.getPrincipal() != null)) {
				if(UserPrincipal.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
					principal = (UserPrincipal)authentication.getPrincipal();

					HttpSession session = request.getSession(false);

					if(session != null) {
						session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, principal.getLocale());
						session.setAttribute(Constants.USER, principal);
						LocaleContextHolder.setLocale(principal.getLocale());
					}
				}
			}
		}

		/**
		 * The default behavior of this method is to return getLocale() on the wrapped request object.
		 *
		 * @return the locale.
		 */
		public Locale getLocale() {
			// Build locale from parameter if exists
			String lang = getParameter("lang");

			if((lang != null) && (lang.length() > 0)) {
				String[] parts = lang.split("_");

				switch(parts.length) {
					case 1:
						return new Locale(parts[0]);

					case 2:
						return new Locale(parts[0], parts[1]);

					case 3:
						return new Locale(parts[0], parts[1], parts[2]);
				}
			}

			// If principal exists, get its default preference
			if(principal != null) {
				return principal.getLocale();
			} else {
				// Fall back to default locale.
				return super.getLocale();
			}
		}

		UserPrincipal getPrincipal() {
			return principal;
		}

		/**
		 * Retrieves the user principal.
		 *
		 * @return the user principal.
		 */
		public java.security.Principal getUserPrincipal() {
			return principal;
		}
	}
}
